using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using MessageLibrary;
using System.Threading;
using System.Net;
using System.Runtime.Serialization.Formatters.Soap;

namespace LeChat
{
    public delegate void UpdateLogInvoker(bool flag);
    public delegate void RtfBoxInvokerDelegate(string text);
    
    public partial class Server : Form
    {
        #region Data Members

        IPAddress address ;
        int port;
        SoapFormatter sf = new SoapFormatter();
        ActiveClients array = new ActiveClients();
        bool logChanged = false;
        bool pendingMessage = false;
        
        #endregion

        public Server()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            
            //registers to the collections events
            array.Changed += new ActiveClients.CollectionChnagedEventHandler(array_Changed);
            array.Added += new ActiveClients.ChangeEventHandler(array_Added);
            array.Removed += new ActiveClients.ChangeEventHandler(array_Removed);
            //updates form display
            btnLogout.Enabled = false;
            this.Icon = Images.Offline;
        }
        /// <summary>
        /// the method that runs when the
        /// ActiveClients.Removed event occurs
        /// </summary>
        /// <param name="c">the client to remove</param>
        void array_Removed(Client c)
        {
            if (listView1.InvokeRequired)
            {
                ActiveClients.ChangeEventHandler ev = new ActiveClients.ChangeEventHandler(array_Removed);
                listView1.Invoke(ev, c);
            }
            else
            {
                //finds the given client in the 
                //listViewItems collection and removes it
                foreach (ListViewItem o in listView1.Items)
                {
                    if (o.Name==c.UserName)
                    {
                        listView1.Items.Remove(o);
                    }
                }
            }
        }
        /// <summary>
        /// the method that runs when the
        /// ActiveClients.Added event occurs
        /// </summary>
        /// <param name="c">the client to add</param>
        void array_Added(Client c)
        {
            
            if (listView1.InvokeRequired)
            {
                ActiveClients.ChangeEventHandler ev = new ActiveClients.ChangeEventHandler(array_Added);
                listView1.Invoke(ev,c);
            }
            else
            {
                //gets the wanted data from the client to a string array
                string[] arr ={ c.UserName, c.LogOnTime.ToShortTimeString(), c.Address };
                //transfers it to a listView item
                ListViewItem lvit = new ListViewItem(arr);
                lvit.Name = c.UserName;
                //inserts the item to the display
                listView1.Items.Add(lvit);
                listView1.Refresh();
            }
            
        }
        /// <summary>
        /// the method that runs when the
        /// ActiveClients.Changed event occurs
        /// </summary>
        /// <param name="arr"></param>
        void array_Changed(List<Client> arr)
        {
            //updates a change has occured in the log
            logChanged = true;
        }
        /// <summary>
        /// the background worker method that preforms
        /// the listening thread for new clients
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            //creates a new listener with the user's data            
            TcpListener listner = new TcpListener(address, port);
            //stops any former actions on this port
            listner.Stop();
            

            try
            {
                
                listner.Start();

                //report: Waiting for client
                listeningBkgWorker.ReportProgress(1);     
            }
            catch(SocketException exc)
            {
                //informs the user that the listner did
                //not start.
                MessageBox.Show("Could not start listening\nPlease check log in infomation","Oh No!",MessageBoxButtons.OK,MessageBoxIcon.Error);
                listeningBkgWorker.ReportProgress(4);
                return;
            }
            //starts the listening loop.
            //it will until cancelation has been called
            while (listeningBkgWorker.CancellationPending == false)
            {
                           
                //wait for client to make contact request
                if (listner.Pending())
                {
                    //lock (this)
                    {
                        
                        Client c;
                        try
                        {
                            c = new Client(listner.AcceptTcpClient());
                        }
                        catch (SocketException exp)
                        {
                            MessageBox.Show(exp.Message, "Source: " + exp.Source);

                            continue;
                        }

                        //reports contact with client
                        listeningBkgWorker.ReportProgress(2);

                        //registers to the clients events
                        c.NewMessage += new Client.MessageEventHandler(c_NewMessage);
                        c.Finished += new Client.FinishedEventHandler(c_Finished);

                        //starts a new listening thread to the client
                        //and sends the tcpClient to the thread
                        c.Start();

                        //waits for the client to send the log in message
                        while (c.UserName==null)
                        {
                            Thread.Sleep(100);
                        }

                        //adds the client to the active clients list
                        array.Add(c);
                        
                    }
                }
                Thread.Sleep(100);

            }
                //reports cancelation            
                e.Cancel = true;
                pendingMessage = true;

                //send a message to the users that
                //the server is logging off
                SendServerMessage("Server is Logging off, GoodBye");
                //wait until all messages has been sent
                while (pendingMessage)
                {
                    Thread.Sleep(50);
                }

                //clear resouces
                listner.Stop();
            
                //report: stopped listening
                listeningBkgWorker.ReportProgress(3);           
             
        }
        /// <summary>
        /// the method for the client.Finished event
        /// 
        /// </summary>
        /// <param name="c"></param>
        void c_Finished(Client c)
        {
            //removes the given client from the array
            array.Remove(c);
        }

        void c_NewMessage(Client c, AbsMessage msg)
        {
            //checks if the recieved message is a
            //statusCheckMessage. if so there is no
            //need to handle it or send it to the
            //other clients.
            if (msg is StatusCheckMessage) return;            
            //updates the message pending status
            //in case the server will be turned off
            pendingMessage = true;
            //handles the message with both a client
            //object and a rtfbox that keeps the log.
            msg.HandleMessage(rtfLog, c);
            //waits for the client to be added to the
            //clients array. 
            //this only occurs if this is a log in message.
            while (array.Contains(c)==false)
            {
                Thread.Sleep(100);
            }
            //calls a method for sending messages
            StartSendingThread(msg);
        }
        

        private void btnLogin_Click(object sender, EventArgs e)
        {
            //creates a new log-in form with the current
            //user's data, if available
            LogInForm lgForm = new LogInForm(address, port);

            lgForm.ShowDialog();

            if (lgForm.Result == DialogResult.OK)
            {
                //transfers the user's data to local
                //data members
                address = lgForm.Address;
                port = lgForm.Port;
                //call for the listening thread to start
                listeningBkgWorker.RunWorkerAsync();
                //update form
                btnLogin.Enabled = false;
                btnLogout.Enabled = true;
            }
            //dispose of the log-in form
            lgForm.Dispose();
        }

        private void btnLogout_Click(object sender, EventArgs e)
        {
            //stops the listening thread
            listeningBkgWorker.CancelAsync();            
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            switch (e.ProgressPercentage)
            {
                case 1:
                    lblStatus.Text = "listening";
                    UpdateLog(true);
                    this.Icon = Images.online;
                    break;
                case 2:
                    lblStatus.Text = "Recieving Client";
                    break;
                case 3:
                    lblStatus.Text = "stopped";
                    UpdateLog(false);
                    this.Icon = Images.Offline;
                    break;
                case 4:
                    lblStatus.Text = "Faild to connect";
                    break;
                default:
                    break;
            }
        }

      
        /// <summary>
        /// a method for sending content messages from
        /// the server itself
        /// </summary>
        /// <param name="txt">the text to send</param>
        public void SendServerMessage(string txt)
        {
            //create a rtfBox for the content message
            RichTextBox rtfBox = new RichTextBox();
            //defines the rtfbox style
            rtfBox.Font = new Font("Tahoma", 11, FontStyle.Italic);
            rtfBox.ForeColor = Color.Black;
            //inserts the message
            rtfBox.Text = txt;
            //creates the new message
            ContentMessage cm = new ContentMessage("Le Server", rtfBox.Rtf);

            rtfBox.Dispose();
            //sends the message
            StartSendingThread(cm);
        }
        /// <summary>
        /// a method that start a new thread for sending
        /// a message
        /// </summary>
        /// <param name="msg">the message to send</param>
        private void StartSendingThread(AbsMessage msg)
        {

            Thread t = new Thread(new ParameterizedThreadStart(SendingThread));
                    t.IsBackground=true;
                    t.Name = "Sending Thread";
                    t.Start(msg);
        }
        /// <summary>
        /// a method that sends a message
        /// for the users in the clients array
        /// </summary>
        /// <param name="o">the message</param>
        private void SendingThread(object o)
        {    
        
            //convert the reference type to a message
            AbsMessage msg = (AbsMessage)o;
            //runs through the client array
            for (int i = 0; i < array.Count ; i++)
            {
                //checks if client is connected
                if (array[i].Running)
                {
                    //sends message    
                    array[i].SendMessage(msg);
                }
            }
            //informs the the message was sent to everyone
            pendingMessage = false;
        
        }

        private void listeningBkgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //stops all the active clients from listening
            array.StopArray();
            //updates form
            btnLogin.Enabled = true;
            btnLogout.Enabled = false;
        }

        private void Server_FormClosing(object sender, FormClosingEventArgs e)
        {
            //ask the user for validation
            DialogResult dr = MessageBox.Show("Are you sure you want to leave?","Leaving so soon?",MessageBoxButtons.YesNo,MessageBoxIcon.Question);
            //cancels closing if needed
            if (dr==DialogResult.No)
            {
                e.Cancel = true;
                return;
            }                      
            //calls for stopping the listening thread
            listeningBkgWorker.CancelAsync();          
            
            
        }

        private void Server_FormClosed(object sender, FormClosedEventArgs e)
        {

            //makes sure there aren't any messages  
            //to send before closing all resources.
            while (pendingMessage)
            {
                Thread.Sleep(50);
                Application.DoEvents();
            }
            //checks if the log has been changed
            //since it was last saved 
            if (logChanged)
            {
                //suggests to save the log                
                DialogResult dr = MessageBox.Show("Do you want to save this log?", "Before leaving", MessageBoxButtons.YesNo);
                if (dr == DialogResult.Yes)
                {
                    SaveLog();
                }
            }
        }
        /// <summary>
        /// preforms a save to the log text to
        /// a rtf file
        /// </summary>
        /// <returns>a value indicating if the file was saved</returns>
        private bool SaveLog()
        {
            //enters today's date to the log file
            saveFileDialog1.FileName = DateTime.Today.ToShortDateString().Replace('/','_') + " Log.rtf";
            //shows the save file dialog
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                //saves the file to the given file name
                rtfLog.SaveFile(saveFileDialog1.FileName);

                return true;
            }
            else return false;
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //calls for the saving method
            if (SaveLog())
            {
                //updates that the log was saved
                logChanged = false;
            }
        }
        /// <summary>
        /// a method for updating the log when the server
        /// has started\stopped listening
        /// </summary>
        /// <param name="flag">a value indicating start\stop</param>
        private void UpdateLog(bool flag)
        {
            if (rtfLog.InvokeRequired)
            {
                UpdateLogInvoker uli = new UpdateLogInvoker(UpdateLog);
                rtfLog.Invoke(uli, flag);
            }
            else
            {
                //finds the end of the chat conversation
                rtfLog.SelectionStart = rtfLog.Text.Length;
                //prints the time of the message
                rtfLog.SelectedText = DateTime.Now.ToShortTimeString();
                //print the message according to the flag
                if (flag)
                {
                    rtfLog.SelectedText = " The Server has started listening. \n";
                }
                else
                    rtfLog.SelectedText = " The Server has stopped listening. \n";


                //scrolls the text box down
                rtfLog.ScrollToCaret();
                //marks a change in the log
                logChanged = true;

            }
        }

         
    }
}